🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / app / src / main / java / com / geeksville / mesh / repository / radio / RadioInterfaceService.kt
Displaying Raw • Download
app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt ui_message_list (be3f882f) Text, 13.38 KB
T8b949e/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787com.geeksville.mesh.repository.radio
Tff7b72import T7ee787android.app.Application
Tff7b72import T7ee787androidx.lifecycle.Lifecycle
Tff7b72import T7ee787androidx.lifecycle.coroutineScope
Tff7b72import T7ee787com.geeksville.mesh.BuildConfig
Tff7b72import T7ee787com.geeksville.mesh.CoroutineDispatchers
Tff7b72import T7ee787com.geeksville.mesh.MeshProtos
Tff7b72import T7ee787com.geeksville.mesh.android.BinaryLogFile
Tff7b72import T7ee787com.geeksville.mesh.android.BuildUtils
Tff7b72import T7ee787com.geeksville.mesh.android.GeeksvilleApplication
Tff7b72import T7ee787com.geeksville.mesh.android.Logging
Tff7b72import T7ee787com.geeksville.mesh.android.prefs.RadioPrefs
Tff7b72import T7ee787com.geeksville.mesh.concurrent.handledLaunch
Tff7b72import T7ee787com.geeksville.mesh.repository.bluetooth.BluetoothRepository
Tff7b72import T7ee787com.geeksville.mesh.repository.network.NetworkRepository
Tff7b72import T7ee787com.geeksville.mesh.util.anonymize
Tff7b72import T7ee787com.geeksville.mesh.util.ignoreException
Tff7b72import T7ee787com.geeksville.mesh.util.toRemoteExceptions
Tff7b72import T7ee787kotlinx.coroutines.CoroutineScope
Tff7b72import T7ee787kotlinx.coroutines.Dispatchers
Tff7b72import T7ee787kotlinx.coroutines.Job
Tff7b72import T7ee787kotlinx.coroutines.cancel
Tff7b72import T7ee787kotlinx.coroutines.channels.BufferOverflow
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableSharedFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableStateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.SharedFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.StateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.asSharedFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.asStateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.launchIn
Tff7b72import T7ee787kotlinx.coroutines.flow.onEach
Tff7b72import T7ee787kotlinx.coroutines.launch
Tff7b72import T7ee787javax.inject.Inject
Tff7b72import T7ee787javax.inject.Singleton
T8b949e/**
* Handles the bluetooth link with a mesh radio device. Does not cache any device state, just does bluetooth comms
* etc...
*
* This service is not exposed outside of this process.
*
* Note - this class intentionally dumb. It doesn't understand protobuf framing etc... It is designed to be simple so it
* can be stubbed out with a simulated version as needed.
*/
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffLongParameterListTa5d6ff"Tb4b4b4)
Tf0883e@Singleton
Tff7b72class T56d364RadioInterfaceService
Tf0883e@Inject
Tff7b72constructorTb4b4b4(
Tff7b72private Tff7b72val Te6edf3contextTb4b4b4: Te6edf3ApplicationTb4b4b4,
Tff7b72private Tff7b72val Te6edf3dispatchersTb4b4b4: Te6edf3CoroutineDispatchersTb4b4b4,
Tff7b72private Tff7b72val Te6edf3bluetoothRepositoryTb4b4b4: Te6edf3BluetoothRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3networkRepositoryTb4b4b4: Te6edf3NetworkRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3processLifecycleTb4b4b4: Te6edf3LifecycleTb4b4b4,
Tff7b72private Tff7b72val Te6edf3radioPrefsTb4b4b4: Te6edf3RadioPrefsTb4b4b4,
Tff7b72private Tff7b72val Te6edf3interfaceFactoryTb4b4b4: Te6edf3InterfaceFactoryTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3Logging Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3_connectionState Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3RadioServiceConnectionStateTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3connectionState Tff7b72= Te6edf3_connectionStateTb4b4b4.Te6edf3asStateFlowTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3_receivedData Tff7b72= Te6edf3MutableSharedFlowTff7b72<Te6edf3ByteArrayTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3receivedDataTb4b4b4: Te6edf3SharedFlowTff7b72<Te6edf3ByteArrayTff7b72> Tff7b72= Te6edf3_receivedData
T8b949e// Thread-safe StateFlow for tracking device address changes
Tff7b72private Tff7b72val Te6edf3_currentDeviceAddressFlow Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3radioPrefsTb4b4b4.Te6edf3devAddrTb4b4b4)
Tff7b72val Te6edf3currentDeviceAddressFlowTb4b4b4: Te6edf3StateFlowTff7b72<Tffa657String?Tff7b72> Tff7b72= Te6edf3_currentDeviceAddressFlowTb4b4b4.Te6edf3asStateFlowTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3logSends Tff7b72= Tff7b72false
Tff7b72private Tff7b72val Te6edf3logReceives Tff7b72= Tff7b72false
Tff7b72private Tff7b72lateinit Tff7b72var Te6edf3sentPacketsLogTb4b4b4: Te6edf3BinaryLogFile T8b949e// inited in onCreate
Tff7b72private Tff7b72lateinit Tff7b72var Te6edf3receivedPacketsLogTb4b4b4: Te6edf3BinaryLogFile
Tff7b72val Te6edf3mockInterfaceAddressTb4b4b4: Tffa657String Tff7b72by Te6edf3lazy Tb4b4b4{ Te6edf3toInterfaceAddressTb4b4b4(Te6edf3InterfaceIdTb4b4b4.Te6edf3MOCKTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4) Tb4b4b4}
T8b949e/** We recreate this scope each time we stop an interface */
Tff7b72var Te6edf3serviceScope Tff7b72= Te6edf3CoroutineScopeTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3IO Tff7b72+ Te6edf3JobTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72private Tff7b72var Te6edf3radioIfTb4b4b4: Te6edf3IRadioInterface Tff7b72= Te6edf3NopInterfaceTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
T8b949e/**
* true if we have started our interface
*
* Note: an interface may be started without necessarily yet having a connection
*/
Tff7b72private Tff7b72var Te6edf3isStarted Tff7b72= Tff7b72false
T8b949e// true if our interface is currently connected to a device
Tff7b72private Tff7b72var Te6edf3isConnected Tff7b72= Tff7b72false
Tff7b72private Tff7b72fun Td2a8ffinitStateListenersTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3bluetoothRepositoryTb4b4b4.Te6edf3state
Tb4b4b4.Te6edf3onEach Tb4b4b4{ Te6edf3state Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Te6edf3stateTb4b4b4.Te6edf3enabledTb4b4b4) Tb4b4b4{
Te6edf3startInterfaceTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Te6edf3radioIf Tff7b72is Te6edf3BluetoothInterfaceTb4b4b4) Tb4b4b4{
Te6edf3stopInterfaceTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3launchInTb4b4b4(Te6edf3processLifecycleTb4b4b4.Te6edf3coroutineScopeTb4b4b4)
Te6edf3networkRepositoryTb4b4b4.Te6edf3networkAvailable
Tb4b4b4.Te6edf3onEach Tb4b4b4{ Te6edf3state Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Te6edf3stateTb4b4b4) Tb4b4b4{
Te6edf3startInterfaceTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Te6edf3radioIf Tff7b72is Te6edf3TCPInterfaceTb4b4b4) Tb4b4b4{
Te6edf3stopInterfaceTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3launchInTb4b4b4(Te6edf3processLifecycleTb4b4b4.Te6edf3coroutineScopeTb4b4b4)
Tb4b4b4}
Tff7b72companion Tff7b72object Tb4b4b4{
Tff7b72private Tff7b72const Tff7b72val Te6edf3HEARTBEAT_INTERVAL_MILLIS Tff7b72= T79c0ff5 Tff7b72* T79c0ff6T79c0ff0 Tff7b72* T79c0ff1T79c0ff0T79c0ff0T79c0ff0L
Tb4b4b4}
Tff7b72private Tff7b72var Te6edf3lastHeartbeatMillis Tff7b72= T79c0ff0L
Tff7b72fun Td2a8ffkeepAliveTb4b4b4(Te6edf3nowTb4b4b4: Tffa657Long Tff7b72= Te6edf3SystemTb4b4b4.Te6edf3currentTimeMillisTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3now Tff7b72- Te6edf3lastHeartbeatMillis Tff7b72> Te6edf3HEARTBEAT_INTERVAL_MILLISTb4b4b4) Tb4b4b4{
Te6edf3infoTb4b4b4(Ta5d6ff"Ta5d6ffSending ToRadio heartbeatTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3heartbeat Tff7b72=
Te6edf3MeshProtosTb4b4b4.Te6edf3ToRadioTb4b4b4.Te6edf3newBuilderTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3setHeartbeatTb4b4b4(Te6edf3MeshProtosTb4b4b4.Te6edf3HeartbeatTb4b4b4.Te6edf3getDefaultInstanceTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3buildTb4b4b4(Tb4b4b4)
Te6edf3handleSendToRadioTb4b4b4(Te6edf3heartbeatTb4b4b4.Te6edf3toByteArrayTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3lastHeartbeatMillis Tff7b72= Te6edf3now
Tb4b4b4}
Tb4b4b4}
T8b949e/** Constructs a full radio address for the specific interface type. */
Tff7b72fun Td2a8fftoInterfaceAddressTb4b4b4(Te6edf3interfaceIdTb4b4b4: Te6edf3InterfaceIdTb4b4b4, Te6edf3restTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657String Tff7b72=
Te6edf3interfaceFactoryTb4b4b4.Te6edf3toInterfaceAddressTb4b4b4(Te6edf3interfaceIdTb4b4b4, Te6edf3restTb4b4b4)
Tff7b72fun Td2a8ffisMockInterfaceTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Te6edf3BuildConfigTb4b4b4.Te6edf3DEBUG Tff7b72|Tff7b72| Tb4b4b4(Te6edf3context Tff7b72as Te6edf3GeeksvilleApplicationTb4b4b4)Tb4b4b4.Te6edf3isInTestLab
T8b949e/**
* Determines whether to default to mock interface for device address. This keeps the decision logic separate and
* easy to extend.
*/
Tff7b72private Tff7b72fun Td2a8ffshouldDefaultToMockInterfaceTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Te6edf3BuildUtilsTb4b4b4.Te6edf3isEmulator
T8b949e/**
* Return the device we are configured to use, or null for none device address strings are of the form:
*
* at
*
* where a is either x for bluetooth or s for serial and t is an interface specific address (macaddr or a device
* path)
*/
Tff7b72fun Td2a8ffgetDeviceAddressTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657String? Tb4b4b4{
T8b949e// If the user has unpaired our device, treat things as if we don't have one
Tff7b72var Te6edf3address Tff7b72= Te6edf3radioPrefsTb4b4b4.Te6edf3devAddr
T8b949e// If we are running on the emulator we default to the mock interface, so we can have some data to show to the
T8b949e// user
Tff7b72if Tb4b4b4(Te6edf3address Tff7b72=Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3shouldDefaultToMockInterfaceTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3address Tff7b72= Te6edf3mockInterfaceAddress
Tb4b4b4}
Tff7b72return Te6edf3address
Tb4b4b4}
T8b949e/**
* Like getDeviceAddress, but filtered to return only devices we are currently bonded with
*
* at
*
* where a is either x for bluetooth or s for serial and t is an interface specific address (macaddr or a device
* path)
*/
Tff7b72fun Td2a8ffgetBondedDeviceAddressTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657String? Tb4b4b4{
T8b949e// If the user has unpaired our device, treat things as if we don't have one
Tff7b72val Te6edf3address Tff7b72= Te6edf3getDeviceAddressTb4b4b4(Tb4b4b4)
Tff7b72return Tff7b72if Tb4b4b4(Te6edf3interfaceFactoryTb4b4b4.Te6edf3addressValidTb4b4b4(Te6edf3addressTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3address
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72null
Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffbroadcastConnectionChangedTb4b4b4(Te6edf3isConnectedTb4b4b4: Tffa657BooleanTb4b4b4, Te6edf3isPermanentTb4b4b4: Tffa657BooleanTb4b4b4) Tb4b4b4{
Te6edf3debugTb4b4b4(Ta5d6ff"Ta5d6ffBroadcasting connection=Tffd700$Te6edf3isConnectedTa5d6ff"Tb4b4b4)
Te6edf3processLifecycleTb4b4b4.Te6edf3coroutineScopeTb4b4b4.Te6edf3launchTb4b4b4(Te6edf3dispatchersTb4b4b4.Te6edf3defaultTb4b4b4) Tb4b4b4{
Te6edf3_connectionStateTb4b4b4.Te6edf3emitTb4b4b4(Te6edf3RadioServiceConnectionStateTb4b4b4(Te6edf3isConnectedTb4b4b4, Te6edf3isPermanentTb4b4b4)Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e// Send a packet/command out the radio link, this routine can block if it needs to
Tff7b72private Tff7b72fun Td2a8ffhandleSendToRadioTb4b4b4(Te6edf3pTb4b4b4: Te6edf3ByteArrayTb4b4b4) Tb4b4b4{
Te6edf3radioIfTb4b4b4.Te6edf3handleSendToRadioTb4b4b4(Te6edf3pTb4b4b4)
Te6edf3emitSendActivityTb4b4b4(Tb4b4b4)
Tb4b4b4}
T8b949e// Handle an incoming packet from the radio, broadcasts it as an android intent
Tff7b72fun Td2a8ffhandleFromRadioTb4b4b4(Te6edf3pTb4b4b4: Te6edf3ByteArrayTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3logReceivesTb4b4b4) Tb4b4b4{
Te6edf3receivedPacketsLogTb4b4b4.Te6edf3writeTb4b4b4(Te6edf3pTb4b4b4)
Te6edf3receivedPacketsLogTb4b4b4.Te6edf3flushTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3radioIf Tff7b72is Te6edf3SerialInterfaceTb4b4b4) Tb4b4b4{
Te6edf3keepAliveTb4b4b4(Te6edf3SystemTb4b4b4.Te6edf3currentTimeMillisTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
T8b949e// ignoreException { debug("FromRadio: ${MeshProtos.FromRadio.parseFrom(p)}") }
Te6edf3processLifecycleTb4b4b4.Te6edf3coroutineScopeTb4b4b4.Te6edf3launchTb4b4b4(Te6edf3dispatchersTb4b4b4.Te6edf3ioTb4b4b4) Tb4b4b4{ Te6edf3_receivedDataTb4b4b4.Te6edf3emitTb4b4b4(Te6edf3pTb4b4b4) Tb4b4b4}
Te6edf3emitReceiveActivityTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72fun Td2a8ffonConnectTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3isConnectedTb4b4b4) Tb4b4b4{
Te6edf3isConnected Tff7b72= Tff7b72true
Te6edf3broadcastConnectionChangedTb4b4b4(Te6edf3isConnected Tff7b72= Tff7b72trueTb4b4b4, Te6edf3isPermanent Tff7b72= Tff7b72falseTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72fun Td2a8ffonDisconnectTb4b4b4(Te6edf3isPermanentTb4b4b4: Tffa657BooleanTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3isConnectedTb4b4b4) Tb4b4b4{
Te6edf3isConnected Tff7b72= Tff7b72false
Te6edf3broadcastConnectionChangedTb4b4b4(Te6edf3isConnected Tff7b72= Tff7b72falseTb4b4b4, Te6edf3isPermanent Tff7b72= Te6edf3isPermanentTb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e/** Start our configured interface (if it isn't already running) */
Tff7b72private Tff7b72fun Td2a8ffstartInterfaceTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3radioIf Tff7b72!is Te6edf3NopInterfaceTb4b4b4) Tb4b4b4{
Te6edf3warnTb4b4b4(Ta5d6ff"Ta5d6ffCan't start interface - Tffd700$Te6edf3radioIfTa5d6ff is already runningTa5d6ff"Tb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72val Te6edf3address Tff7b72= Te6edf3getBondedDeviceAddressTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3address Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3warnTb4b4b4(Ta5d6ff"Ta5d6ffNo bonded mesh radio, can't start interfaceTa5d6ff"Tb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3infoTb4b4b4(Ta5d6ff"Ta5d6ffStarting radio Tffd700${Te6edf3addressTb4b4b4.Te6edf3anonymizeTffd700}Ta5d6ff"Tb4b4b4)
Te6edf3isStarted Tff7b72= Tff7b72true
Tff7b72if Tb4b4b4(Te6edf3logSendsTb4b4b4) Tb4b4b4{
Te6edf3sentPacketsLog Tff7b72= Te6edf3BinaryLogFileTb4b4b4(Te6edf3contextTb4b4b4, Ta5d6ff"Ta5d6ffsent_log.pbTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3logReceivesTb4b4b4) Tb4b4b4{
Te6edf3receivedPacketsLog Tff7b72= Te6edf3BinaryLogFileTb4b4b4(Te6edf3contextTb4b4b4, Ta5d6ff"Ta5d6ffreceive_log.pbTa5d6ff"Tb4b4b4)
Tb4b4b4}
Te6edf3radioIf Tff7b72= Te6edf3interfaceFactoryTb4b4b4.Te6edf3createInterfaceTb4b4b4(Te6edf3addressTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffstopInterfaceTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3r Tff7b72= Te6edf3radioIf
Te6edf3infoTb4b4b4(Ta5d6ff"Ta5d6ffstopping interface Tffd700$Te6edf3rTa5d6ff"Tb4b4b4)
Te6edf3isStarted Tff7b72= Tff7b72false
Te6edf3radioIf Tff7b72= Te6edf3interfaceFactoryTb4b4b4.Te6edf3nopInterface
Te6edf3rTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
T8b949e// cancel any old jobs and get ready for the new ones
Te6edf3serviceScopeTb4b4b4.Te6edf3cancelTb4b4b4(Ta5d6ff"Ta5d6ffstopping interfaceTa5d6ff"Tb4b4b4)
Te6edf3serviceScope Tff7b72= Te6edf3CoroutineScopeTb4b4b4(Te6edf3DispatchersTb4b4b4.Te6edf3IO Tff7b72+ Te6edf3JobTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3logSendsTb4b4b4) Tb4b4b4{
Te6edf3sentPacketsLogTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3logReceivesTb4b4b4) Tb4b4b4{
Te6edf3receivedPacketsLogTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Tb4b4b4}
T8b949e// Don't broadcast disconnects if we were just using the nop device
Tff7b72if Tb4b4b4(Te6edf3r Tff7b72!is Te6edf3NopInterfaceTb4b4b4) Tb4b4b4{
Te6edf3onDisconnectTb4b4b4(Te6edf3isPermanent Tff7b72= Tff7b72trueTb4b4b4) T8b949e// Tell any clients we are now offline
Tb4b4b4}
Tb4b4b4}
T8b949e/**
* Change to a new device
*
* @return true if the device changed, false if no change
*/
Tff7b72private Tff7b72fun Td2a8ffsetBondedDeviceAddressTb4b4b4(Te6edf3addressTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3getBondedDeviceAddressTb4b4b4(Tb4b4b4) Tff7b72=Tff7b72= Te6edf3address Tff7b72&Tff7b72& Te6edf3isStartedTb4b4b4) Tb4b4b4{
Te6edf3warnTb4b4b4(Ta5d6ff"Ta5d6ffIgnoring setBondedDevice Tffd700${Te6edf3addressTb4b4b4.Te6edf3anonymizeTffd700}Ta5d6ff, because we are already using that deviceTa5d6ff"Tb4b4b4)
Tff7b72false
Tb4b4b4} Tff7b72else Tb4b4b4{
T8b949e// Record that this use has configured a new radio
Te6edf3GeeksvilleApplicationTb4b4b4.Te6edf3analyticsTb4b4b4.Te6edf3trackTb4b4b4(Ta5d6ff"Ta5d6ffmesh_bondTa5d6ff"Tb4b4b4)
T8b949e// Ignore any errors that happen while closing old device
Te6edf3ignoreException Tb4b4b4{ Te6edf3stopInterfaceTb4b4b4(Tb4b4b4) Tb4b4b4}
T8b949e// The device address "n" can be used to mean none
Te6edf3debugTb4b4b4(Ta5d6ff"Ta5d6ffSetting bonded device to Tffd700${Te6edf3addressTb4b4b4.Te6edf3anonymizeTffd700}Ta5d6ff"Tb4b4b4)
T8b949e// Stores the address if non-null, otherwise removes the pref
Te6edf3radioPrefsTb4b4b4.Te6edf3devAddr Tff7b72= Te6edf3address
Te6edf3_currentDeviceAddressFlowTb4b4b4.Te6edf3value Tff7b72= Te6edf3address
T8b949e// Force the service to reconnect
Te6edf3startInterfaceTb4b4b4(Tb4b4b4)
Tff7b72true
Tb4b4b4}
Tff7b72fun Td2a8ffsetDeviceAddressTb4b4b4(Te6edf3deviceAddrTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Te6edf3toRemoteExceptions Tb4b4b4{ Te6edf3setBondedDeviceAddressTb4b4b4(Te6edf3deviceAddrTb4b4b4) Tb4b4b4}
T8b949e/**
* If the service is not currently connected to the radio, try to connect now. At boot the radio interface service
* will not connect to a radio until this call is received.
*/
Tff7b72fun Td2a8ffconnectTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3toRemoteExceptions Tb4b4b4{
T8b949e// We don't start actually talking to our device until MeshService binds to us - this prevents
T8b949e// broadcasting connection events before MeshService is ready to receive them
Te6edf3startInterfaceTb4b4b4(Tb4b4b4)
Te6edf3initStateListenersTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72fun Td2a8ffsendToRadioTb4b4b4(Te6edf3aTb4b4b4: Te6edf3ByteArrayTb4b4b4) Tb4b4b4{
T8b949e// Do this in the IO thread because it might take a while (and we don't care about the result code)
Te6edf3serviceScopeTb4b4b4.Te6edf3handledLaunch Tb4b4b4{ Te6edf3handleSendToRadioTb4b4b4(Te6edf3aTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72val Te6edf3_meshActivity Tff7b72=
Te6edf3MutableSharedFlowTff7b72<Te6edf3MeshActivityTff7b72>Tb4b4b4(
Te6edf3replay Tff7b72= T79c0ff0Tb4b4b4, T8b949e// No replay needed for event-like emissions
Te6edf3extraBufferCapacity Tff7b72= T79c0ff1Tb4b4b4, T8b949e// Buffer one event to avoid loss on rapid emissions
Te6edf3onBufferOverflow Tff7b72= Te6edf3BufferOverflowTb4b4b4.Te6edf3DROP_OLDESTTb4b4b4, T8b949e// Drop oldest if buffer overflows
Tb4b4b4)
Tff7b72val Te6edf3meshActivityTb4b4b4: Te6edf3SharedFlowTff7b72<Te6edf3MeshActivityTff7b72> Tff7b72= Te6edf3_meshActivityTb4b4b4.Te6edf3asSharedFlowTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffemitSendActivityTb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// Use tryEmit for SharedFlow as it's non-blocking
Tff7b72val Te6edf3emitted Tff7b72= Te6edf3_meshActivityTb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3MeshActivityTb4b4b4.Te6edf3SendTb4b4b4)
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3emittedTb4b4b4) Tb4b4b4{
Te6edf3debugTb4b4b4(Ta5d6ff"Ta5d6ffMeshActivity.Send event was not emitted due to buffer overflow or no collectorsTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffemitReceiveActivityTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3emitted Tff7b72= Te6edf3_meshActivityTb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3MeshActivityTb4b4b4.Te6edf3ReceiveTb4b4b4)
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3emittedTb4b4b4) Tb4b4b4{
Te6edf3debugTb4b4b4(Ta5d6ff"Ta5d6ffMeshActivity.Receive event was not emitted due to buffer overflow or no collectorsTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tff7b72sealed Tff7b72class T56d364MeshActivity Tb4b4b4{
Tff7b72data Tff7b72object T56d364Send Tb4b4b4: Te6edf3MeshActivityTb4b4b4(Tb4b4b4)
Tff7b72data Tff7b72object T56d364Receive Tb4b4b4: Te6edf3MeshActivityTb4b4b4(Tb4b4b4)
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.13s